﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace MyRaceGame.GameLogic.InputControl
{
    public class InputController
    {
        public InputController()
        {
            moves = new Move[]
            {
                new Move("Run1", Direction.Right, Direction.Left),
                new Move("Run2", Direction.Left, Direction.Right),
            };

            moveList = new MoveList(moves);
            inputManager = new InputManager(PlayerIndex.One, moveList.LongestMoveLength);
            playerMoves = new Move[1];
            playerMoveTimes = new TimeSpan[1];

            previousKeyboard = Keyboard.GetState();
            previousMouse = Mouse.GetState();
        }

        public void Update(GameTime gameTime)
        {
            previousKeyboard = currentKeyboard;
            currentKeyboard = Keyboard.GetState();
            previousMouse = currentMouse;
            currentMouse = Mouse.GetState();

            if (Clicked != null)
            {
                if (clickedRight)
                    Clicked(true, new Vector2(currentMouse.X, currentMouse.Y));
                else if (clickedLeft)
                    Clicked(false, new Vector2(currentMouse.X, currentMouse.Y));
            }

            if (MouseMoved != null && mouseMoved)
                MouseMoved(new Vector2(currentMouse.X, currentMouse.Y));

            if (!checkCombo)
                return;

            // Expire old moves.
            if (gameTime.TotalGameTime - playerMoveTimes[0] > MoveTimeOut)
            {
                playerMoves[0] = null;
            }

            // Get the updated input manager.
            inputManager.Update(gameTime);

            // Detection and record the current player's most recent move.
            Move newMove = moveList.DetectMove(inputManager);
            if (newMove != null)
            {
                playerMoves[0] = newMove;
                playerMoveTimes[0] = gameTime.TotalGameTime;

                if (newMove.Name == "Run1" || newMove.Name == "Run2")
                {
                    acc = gameTime.ElapsedGameTime.Milliseconds / 2000f * accFactor;
                    combo = true;
                }
            }

            if (combo)
                combo = false;
            else
                acc -= gameTime.ElapsedGameTime.Milliseconds / 3000.0f * decFactor;

            speed += acc;

            if (speed >= maxSpeed)
                speed = maxSpeed;
            else if (speed <= 0)
            {
                speed = 0;
                acc = 0;
            }
        }

        public bool KeyPressed(Keys k)
        {
            return previousKeyboard.IsKeyDown(k) && currentKeyboard.IsKeyUp(k);
        }

        public bool IsKeyDown(Keys k)
        {
            return currentKeyboard.IsKeyDown(k);
        }

        public bool IsKeyUp(Keys k)
        {
            return currentKeyboard.IsKeyUp(k);
        }

        public event ClickEventHandler Clicked;
        public event MouseMoveEventHandler MouseMoved;

        #region Properties

        bool mouseMoved
        {
            get { return previousMouse.X != currentMouse.X || previousMouse.Y != currentMouse.Y; }
        }

        bool clickedRight
        {
            get
            {
                return previousMouse.RightButton == ButtonState.Pressed &&
                    currentMouse.RightButton == ButtonState.Released;
            }
        }

        bool clickedLeft
        {
            get
            {
                return previousMouse.LeftButton == ButtonState.Pressed &&
                    currentMouse.LeftButton == ButtonState.Released;
            }
        }

        /// <summary>
        /// Gets mouse displacement in x direction
        /// </summary>
        public float MouseDeltaX
        {
            get { return (float)previousMouse.X - (float)currentMouse.X; }
        }

        /// <summary>
        /// Gets mouse displacement in y direction
        /// </summary>
        public float MouseDeltaY
        {
            get { return (float)previousMouse.Y - (float)currentMouse.Y; }
        }

        /// <summary>
        /// Gets speed
        /// </summary>
        public float Speed
        {
            get { return speed; }
        }

        /// <summary>
        /// Gets acceleration
        /// </summary>
        public float Acceleration
        {
            get { return acc; }
        }

        /// <summary>
        /// Gets or sets upper limit of speed
        /// </summary>
        public float MaxSpeed
        {
            get { return maxSpeed; }
            set { maxSpeed = value; }
        }

        /// <summary>
        /// Gets or sets acceleration factor
        /// </summary>
        public float AccFactor
        {
            get { return accFactor; }
            set { accFactor = value; }
        }

        /// <summary>
        /// Gets or sets decceleration factor
        /// </summary>
        public float DecFactor
        {
            get { return decFactor; }
            set { decFactor = value; }
        }

        /// <summary>
        /// Gets or sets wheter controller checks for combo
        /// </summary>
        public bool CheckCombo
        {
            get { return checkCombo; }
            set { checkCombo = value; }
        }

        #endregion

        #region Fields

        float acc = 0f, speed = 0f;       
        float maxSpeed = 3500f;
        float accFactor = 0.7f;
        float decFactor = 1f;
        KeyboardState previousKeyboard, currentKeyboard;
        MouseState previousMouse, currentMouse;
        bool combo = false;
        bool checkCombo = true;

        #endregion

        #region Input Manager Fields

        InputManager inputManager;
        Move[] moves, playerMoves;
        MoveList moveList;
        TimeSpan[] playerMoveTimes;
        readonly TimeSpan MoveTimeOut = TimeSpan.FromSeconds(1);
        
        #endregion
    }

    public delegate void ClickEventHandler(bool right, Vector2 position);
    public delegate void MouseMoveEventHandler(Vector2 position);
}
